package snowflake

/*
 * snow flake to generate uuid.
 */
import (
	"fmt"
	"sync"
	"time"
)

/**
* @brief 分布式id生成类
* 64bit id: 0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000
*           ||                                                           ||     ||     |  |              |
*           |└---------------------------时间戳(41)-----------------------┘ └中心(5)-┘└机器(5)┘ └序列号(12)┘
*         不用
* SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心(datacenter)ID和机器ID(worker id)作区分),
* 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右.
 */

// const variable enum.
const (
	/*
	 * 开始时间截 (2018-01-01 00:00:00.000)
	 */
	startTime                  int64 = 1514736000000
	baseValue                  int64 = -1
	workerIdBitSize            int64 = 5
	dataCenterBitSize          int64 = 5
	sequenceBitSize            int64 = 12
	maxWorkerId                int64 = baseValue ^ (baseValue << workerIdBitSize)
	maxDataCenterId            int64 = baseValue ^ (baseValue << dataCenterBitSize)
	timeLeftShiftBitSize       int64 = dataCenterBitSize + workerIdBitSize + sequenceBitSize
	dataCenterLeftShiftBitSize int64 = workerIdBitSize + sequenceBitSize
	sequenceMask               int64 = baseValue ^ (baseValue << sequenceBitSize)
	timeMask                   int64 = baseValue ^ (baseValue << timeLeftShiftBitSize)
)

// IdWorker class.
type SnowflakeIdWorker struct {
	workerId      int64
	dataCenterId  int64
	sequence      int64
	lastTimestamp int64
	idLock        sync.Mutex
}

// NewIdWorker New Idworker
func NewIdWorker(workerId, dataCenterId int64) *SnowflakeIdWorker {
	idWorker := new(SnowflakeIdWorker)
	if idWorker.InitIdWorker(workerId, dataCenterId) != nil {
		return nil
	} else {
		return idWorker
	}
}

// InitIdWorker Init IdWorker.
func (sw *SnowflakeIdWorker) InitIdWorker(workerId, dataCenterId int64) error {
	sw.sequence = 0
	sw.lastTimestamp = -1

	// init worker id and dataCenter id.
	sw.workerId = workerId
	sw.dataCenterId = dataCenterId

	// Max value check
	if sw.workerId < 0 || sw.workerId > maxWorkerId {
		return fmt.Errorf("workerId[%v] is less than 0 or greater than maxWorkerId[%v]", workerId, dataCenterId)
	}

	if sw.dataCenterId < 0 || sw.dataCenterId > maxDataCenterId {
		return fmt.Errorf("dataCenterId[%d] is less than 0 or greater than maxDataCenterId[%d]", workerId, dataCenterId)
	}

	return nil
}

// getCurrentMilliSecond get current millisecond
func getCurrentMilliSecond() int64 {
	return time.Now().UnixNano() / 1e6
}

// NextId generate Next Id.
func (sw *SnowflakeIdWorker) NextId() (int64, error) {
	sw.idLock.Lock()
	defer sw.idLock.Unlock()

	timestamp := getCurrentMilliSecond()
	if timestamp < sw.lastTimestamp {
		return -1, fmt.Errorf("generate id for %d millisecond error", sw.lastTimestamp-timestamp)
	}

	if timestamp == sw.lastTimestamp {
		sw.sequence = (sw.sequence + 1) & sequenceMask
		if sw.sequence == 0 {
			timestamp = sw.nextMilliSecond()
		}
	} else {
		sw.sequence = 0
	}
	sw.lastTimestamp = timestamp

	// id composition
	id := ((timestamp - startTime) << timeLeftShiftBitSize) | // timestamp
		(sw.dataCenterId << dataCenterLeftShiftBitSize) | // dataCenterId
		(sw.workerId << sequenceBitSize) | sw.sequence // workerId | sequence

	return id, nil
}

// nextMilliSecond next milliSecond, must not the same time
func (sw *SnowflakeIdWorker) nextMilliSecond() int64 {
	timestamp := getCurrentMilliSecond()
	for timestamp <= sw.lastTimestamp {
		timestamp = getCurrentMilliSecond()
	}
	return timestamp
}

// GetTimeMask time mask
func (sw *SnowflakeIdWorker) GetTimeMask() int64 {
	return 1 << timeLeftShiftBitSize
}

// global object.
var SnowflakeIdWorkerObj SnowflakeIdWorker
